﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using ComponentAce.Compression.ZipForge;
using CRC32c;
using Microsoft.Win32;

namespace APBR_Patch_Mirror
{
    public partial class MainForm : Form
    {
        //Defined Info = new Defined();
        string WorkingDir;
        public static string report = "0";
        public static string Progname = "APB Repair - 2.0" + " (Build " + ProgUpdater.version + ") ";

        public MainForm()
        {
            Program.LoadASM();
            InitializeComponent();
            this.Text = Progname + ProgUpdater.buildDate;
            CheckForIllegalCrossThreadCalls = false;
            GetPath();
        }

        void GetPath()
        {
            string msg = "\nPlease select your APB Directory(Click No)";
            Info.APB_Path = msg;
            string readpath = Environment.SystemDirectory + "\\apb_path";
            string CPUType = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
            RegistryKey regKey = Registry.LocalMachine;

            if (CPUType.Contains("64")) regKey = regKey.CreateSubKey("SOFTWARE\\Wow6432Node\\GamersFirst\\APB\\APB Reloade");

            Info.APB_Path = (string)regKey.GetValue("Installation Path");

            APBDirectory.Description = "Please Select APB: Reloaded Folder";
            try
            {
                try
                {
                    Info.APB_Path = File.ReadAllText(readpath);
                    if (Info.APB_Path == "" || Info.APB_Path == null) Info.APB_Path = msg + "\\";
                }
                catch { Info.APB_Path = msg; }

                if (Info.APB_Path.Contains(msg))
                {
                    APBDirectory.ShowDialog();
                    Info.APB_Path = APBDirectory.SelectedPath;
                    File.WriteAllText(readpath, Info.APB_Path);

                    if (Info.APB_Path == "") Info.APB_Path = msg + "\\";
                }

                if (MessageBox.Show("Is this the correct path?\n" + Info.APB_Path, "Confirm APR:Reloaded Path", MessageBoxButtons.YesNo) == DialogResult.Yes)
                {
                    if (!Info.APB_Path.Contains(msg))
                    {
                        Directory.SetCurrentDirectory(Info.APB_Path);
                        new Task(Begin).Start();
                    }
                    else
                    {
                        MessageBox.Show("Empty String: APB_Path\nExiting Program");
                        Environment.Exit(-1);
                    }
                }
                else
                {
                    APBDirectory.ShowDialog();
                    Info.APB_Path = APBDirectory.SelectedPath;
                    File.WriteAllText(readpath, Info.APB_Path);

                    if (!Info.APB_Path.Contains(msg))
                    {
                        Directory.SetCurrentDirectory(Info.APB_Path);
                        new Task(Begin).Start();
                    }
                    else GetPath();
                }
            }
            catch (Exception e)
            {
                if (MessageBox.Show("You're a god damned retard. Try again.", "Retard detected", MessageBoxButtons.OK) == DialogResult.OK)
                    GetPath();
                else
                    Environment.Exit(-1);
            }
        }

        Point mouseDownPoint = Point.Empty;
        private void MainForm_MouseDown(object sender, MouseEventArgs e) { mouseDownPoint = new Point(e.X, e.Y); }
        private void MainForm_MouseUp(object sender, MouseEventArgs e) { mouseDownPoint = Point.Empty; }
        private void MainForm_MouseMove(object sender, MouseEventArgs e)
        {
            if (mouseDownPoint.IsEmpty)
                return;
            Form f = sender as Form;
            f.Location = new Point(f.Location.X + (e.X - mouseDownPoint.X), f.Location.Y + (e.Y - mouseDownPoint.Y));
        }

        void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Environment.Exit(-1);
        }

        void Fixet_Click(object sender, EventArgs e)
        {
            Fixet.Enabled = false;
            redbutton.BringToFront();
            LogView.Clear();
            new Thread(CheckAPBR).Start();
        }

        void Exit_Click(object sender, EventArgs e)
        {
            Environment.Exit(-1);
        }

        void WriteToView(string text)
        {
            if (InvokeRequired)
            {
                Invoke((Action<string>)WriteToView, text);
            }
            else
            {
                LogView.AppendText(text + "\n");
                LogView.ScrollToCaret();
            }
        }

        void WriteToDL(string text)
        {
            string[] test = text.Split('\\', '/', '\"');
            string test2 = test[test.Length - 1];
            if (InvokeRequired)
            {
                Invoke((Action<string>)WriteToDL, test2);
            }
            else
            {
                DLlog.AppendText(test2 + "\n");
                DLlog.ScrollToCaret();
            }
        }

        void Begin()
        {
            try
            {
                WriteToView("APB:R Path: " + Info.APB_Path + "\n---------------\n");
                Fixet.Enabled = false;
                redbutton.BringToFront();
                while (File.Exists("APB Repair.bak")) { try { File.Delete("APB Repair.bak"); } catch { } }
                WriteToView(new WebClient().DownloadString("http://whatshorrible.com/APB_Reloaded/APB_Repair/News.php"));
                int verify = Int16.Parse(new WebClient().DownloadString("http://whatshorrible.com/APB_Reloaded/APB_Repair/Version.php"));

                if (ProgUpdater.version != verify) { Application.Run(new ProgUpdater()); }
                GetMirrors();
                StartCheck();
                CheckMirrors();
                Info.LauncherXML = new read.LauncherSyncer(Info.LauncherFiles);
                Info.ClientXML = new read.ClientSyncer(Info.ClientFiles);
            }
            catch (Exception e)
            {
                Info.error(e);
            }
            Fixet.Enabled = true;
            redbutton.SendToBack();
        }

        void GetMirrors()
        {
            if (File.Exists("Launcher" + Info.RemoteL_Ver + ".xml" + ".zip")) { File.Delete("Launcher" + Info.RemoteL_Ver + ".xml" + ".zip"); }
            if (File.Exists("Client" + Info.RemoteC_Ver + ".xml" + ".zip")) { File.Delete("Client" + Info.RemoteC_Ver + ".xml" + ".zip"); }
            using (var wc = new WebClient())
            {
                var uri = new Uri("http://whatshorrible.com/APB_Reloaded/APB_Repair/Mirrors.php");
                var temp = wc.DownloadString(uri);
                Info.Webmirrors = temp.Split(' ');
            }
        }

        void StartCheck()
        {
            WorkingDir = Info.APB_Path + "\\";
            string dstring = new WebClient().DownloadString(Info.G1 + Info.Patches);
            string[] words = dstring.Split('"');

            Info.RemoteC_Ver = words[11]; Info.ClientFilesZip = "Client" + Info.RemoteC_Ver + ".xml.zip";
            Info.RemoteL_Ver = words[7]; Info.LauncherFilesZip = "Launcher" + Info.RemoteL_Ver + ".xml.zip";

            new WebClient().DownloadFile(Info.G1 + Info.Patches, Info.Patches);

            if (File.Exists(Info.Patches) == true & File.Exists(Info.ClientFiles) == true & File.Exists(Info.LauncherFiles) == true)
            {
                read.XML("ClientFiles.xml", 2);
                if (Info.LocalC_Ver == Info.RemoteC_Ver)
                {
                    WriteToView("Checking Client Version " + Info.LocalC_Ver + "... VALID");
                }
                else
                {
                    WriteToView("Checking Client Version... INVALID");
                    Info.filePath = Info.ClientFilesZip;
                    Fetch(Info.G1 + Info.ClientFilesZip, Info.ClientFilesZip);
                }

                read.XML2("LauncherFiles.xml", 2);
                if (Info.LocalL_Ver == Info.RemoteL_Ver)
                {
                    WriteToView("Checking Launcher Version " + Info.LocalL_Ver + "... VALID");
                }
                else
                {
                    WriteToView("Checking Launcher Version... INVALID");
                    Info.filePath = Info.LauncherFilesZip;
                    Fetch(Info.G1 + Info.LauncherFilesZip, Info.LauncherFilesZip);
                }
            }
            else
            {
                read.XML(Info.Patches, 8);
                if (!File.Exists(Info.ClientFiles))
                {
                    WriteToView(Info.ClientFiles + " is missing... Attempting to retrieve");
                    Info.filePath = Info.ClientFilesZip;
                    Fetch(Info.G1 + Info.ClientFilesZip, Info.ClientFilesZip);
                }

                read.XML2(Info.Patches, 8);
                if (!File.Exists(Info.LauncherFiles))
                {
                    WriteToView(Info.LauncherFiles + " is missing... Attempting to retrieve");
                    Info.filePath = Info.LauncherFilesZip;
                    Fetch(Info.G1 + Info.LauncherFilesZip, Info.LauncherFilesZip);
                }
                StartCheck();
            }
        }

        void CheckMirrors()
        {
            for (int interger = 0; interger < Info.Webmirrors.Length; interger++)
            {
                try
                {
                    WebClient webClient = new WebClient();
                    webClient.DownloadData(Info.Webmirrors[interger].ToString() + "Patches.xml");
                    WriteToView("Mirror " + Info.Webmirrors[interger].ToString() + " returned OK.");
                    webClient.Dispose();
                }
                catch (Exception e)
                {
                    WriteToView("\nMirror " + Info.Webmirrors[interger].ToString() + " returned ERROR.");
                    WriteToView("Program will not work propperly until the problematic mirrors are fixed!!");
                    WriteToView("Contact a developer for assistance.");
                    Info.error(e);
                }
            }
        }

        void Extract()
        {
            string client = "Client" + Info.RemoteC_Ver + ".xml";
            string launcher = "Launcher" + Info.RemoteL_Ver + ".xml";
            ZipForge zip = new ZipForge();
            zip.FileName = Info.curExtract;
            zip.BaseDir = Path.GetDirectoryName(Info.filePath);
            if (zip.BaseDir == "") zip.BaseDir = Directory.GetCurrentDirectory();
            try
            {
                zip.OpenArchive();
                zip.DeleteFiles("*.sig");                
                zip.ExtractFiles("*");
                zip.CloseArchive();

                if (File.Exists(client)) File.Move(client, "ClientFiles.xml");
                else if (File.Exists(launcher)) File.Move(launcher, "LauncherFiles.xml");

                File.Delete(Info.curExtract);
            }
            catch (Exception e)
            {
                WriteToView(e.ToString() + Environment.NewLine);
                Extract();
            }
        }

        void CheckAPBR()
        {
            WriteToView("Checking APB: Reloaded Install...  ");
            try
            {
                #region Launcher
                long crc = 0;
                for (Info.integer = 0; Info.integer < Info.LauncherXML._LauncherFiles.Count; Info.integer++)
                {
                    progressBar1.Value = Info.integer;
                    progressBar1.Maximum = Info.LauncherXML._LauncherFiles.Count;
                    SetProgressBarText(progressBar1, ProgressBarTextLocation.Centered);

                    Info.filePath = Info.LauncherXML._LauncherFiles[Info.integer].FileName;

                    WriteToView(Info.LauncherXML._LauncherFiles[Info.integer].FileName);
                    if (File.Exists(Info.filePath))
                    {
                        Crc32 crc32 = new Crc32(); var path = Info.ConvertPath(Info.APB_Path + "\\" + Info.filePath, true);
                        Int64 FileCrc = Info.LauncherXML._LauncherFiles[Info.integer].Crc;
                        using (FileStream f = File.Open(path, FileMode.Open)) crc32.ComputeHash(f);
                        crc = crc32.CrcValue;

                        if (crc != FileCrc)
                        {
                            PrepFile(Info.LauncherXML._LauncherFiles);
                        }

                        if (crc != Info.LauncherXML._LauncherFiles[Info.integer].Crc)
                        {
                            File.AppendAllText("Error.log", DateTime.Now + "   Check: FAILED   " + "\"" + Info.RemoteFile + "\"  " + Info.LauncherXML._LauncherFiles[Info.integer].Crc + Environment.NewLine);
                            Console.ForegroundColor = ConsoleColor.Cyan;
                            Console.Write("Sanity Check: ");
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("FAILED                   ");
                            Console.ForegroundColor = ConsoleColor.White;
                        }
                    }
                    else
                    {
                        PrepFile(Info.LauncherXML._LauncherFiles);

                        if (crc != Info.LauncherXML._LauncherFiles[Info.integer].Crc)
                        {
                            File.AppendAllText("Error.log", DateTime.Now + "    Check: FAILED    " + "\"" + Info.RemoteFile + "\"  " + Info.LauncherXML._LauncherFiles[Info.integer].Crc + Environment.NewLine);
                            Console.ForegroundColor = ConsoleColor.Cyan;
                            Console.Write("Sanity Check: ");
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("FAILED                   ");
                            Console.ForegroundColor = ConsoleColor.White;
                        }
                    }
                } 
                #endregion

                #region Client
                progressBar1.Value = 0;
                Info.integer = 0;
                for (Info.integer = 0; Info.integer < Info.ClientXML._ClientFiles.Count; Info.integer++)
                {
                    progressBar1.Value = Info.integer;
                    progressBar1.Maximum = Info.ClientXML._ClientFiles.Count;
                    SetProgressBarText(progressBar1, ProgressBarTextLocation.Centered);

                    Info.filePath = Info.ClientXML._ClientFiles[Info.integer].FileName;

                    WriteToView(Info.ClientXML._ClientFiles[Info.integer].FileName);

                    if (File.Exists(Info.filePath))
                    {
                        Crc32 crc32 = new Crc32();
                        using (FileStream f = File.Open(Info.filePath, FileMode.Open)) crc32.ComputeHash(f);
                        crc = (Int64)crc32.CrcValue;

                        if (crc != Info.ClientXML._ClientFiles[Info.integer].Crc)
                        {
                            PrepFile(Info.ClientXML._ClientFiles);
                        }

                        if (crc != Info.ClientXML._ClientFiles[Info.integer].Crc)
                        {
                            File.AppendAllText("Error.log", DateTime.Now + "    Sanity Check: FAILED    " + "\"" + Info.RemoteFile + "\"  " + Info.ClientXML._ClientFiles[Info.integer].Crc + Environment.NewLine);
                            Console.ForegroundColor = ConsoleColor.Cyan;
                            Console.Write("Sanity Check: ");
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("FAILED                   ");
                            Console.ForegroundColor = ConsoleColor.White;
                        }
                    }
                    else
                    {
                        PrepFile(Info.ClientXML._ClientFiles);

                        if (crc != Info.ClientXML._ClientFiles[Info.integer].Crc)
                        {
                            File.AppendAllText("Error.log", DateTime.Now + "    Sanity Check: FAILED    " + "\"" + Info.RemoteFile + "\"  " + Info.ClientXML._ClientFiles[Info.integer].Crc + Environment.NewLine);
                            Console.ForegroundColor = ConsoleColor.Cyan;
                            Console.Write("Sanity Check: ");
                            Console.ForegroundColor = ConsoleColor.Red;
                            Console.WriteLine("FAILED                   ");
                            Console.ForegroundColor = ConsoleColor.White;
                        }
                    }
                } 
                #endregion
            }
            catch (Exception e)
            {                
                Info.error(e);
                Environment.Exit(-1);
            }
            progressBar1.Value = 0;
            Fixet.Enabled = true;
        }

        void GrabFile(string url)
        {
            try
            {
                int count; DateTime marker = DateTime.Now;
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.AllowAutoRedirect = true;
                request.UserAgent = APBR_Patch_Mirror.MainForm.Progname;

                WebResponse response = request.GetResponse();
                int contlength = contlength = 1024;
                try { contlength = int.Parse(response.Headers.Get("Content-Length")); }
                catch { }

                byte[] buffer = new byte[contlength];
                FileStream fs = new FileStream(Info.ConvertPath(Info.curExtract), FileMode.Create);
                do
                {
                    count = response.GetResponseStream().Read(buffer, 0, buffer.Length);
                    fs.Write(buffer, 0, count);

                    double kbsec = count / DateTime.Now.Subtract(marker).TotalSeconds;
                    UpdateDLbar(count, contlength, kbsec);

                } while (count != 0);
                response.Close(); request.Abort(); fs.Close();
            }
            catch (Exception e)
            {
                WriteToDL("Error: Skipping " + Info.curExtract);
                File.AppendAllText("Skipped.log", "Error: Skipping " + Info.curExtract + Environment.NewLine);
            }

            GC.Collect();
            DLbar.Value = 0;
        }

        void Fetch(string url, string saveAs)
        {
            try
            {
                WriteToDL("" + Info.curExtract + "");
                Info.curExtract = saveAs;

                GrabFile(url);

                if (Info.curExtract.EndsWith(".zip")) Extract();

                progressBar1.Value = 0;
            }
            catch (Exception e)
            {
                Info.error(e);
            }
        }

        void UpdateDLbar(int count, int length, double kbs)
        {
            if (InvokeRequired)
            {
                Invoke((MethodInvoker)delegate() { DLbar.Maximum = length; });

                if ((DLbar.Value + count) <= DLbar.Maximum)
                {
                    Invoke((MethodInvoker)delegate() { DLbar.Value += count; });
                    SetProgressBarText(DLbar, ProgressBarTextLocation.Centered, true, kbs);

                    Thread.Sleep(25);
                }
                else Invoke((MethodInvoker)delegate() { DLbar.Value = DLbar.Maximum; });
            }
            else
            {
                DLbar.Maximum = length;
                if ((DLbar.Value + count) <= DLbar.Maximum) DLbar.Value += count;
                else DLbar.Value = DLbar.Maximum;
                SetProgressBarText(DLbar, ProgressBarTextLocation.Centered, true, kbs);
                Thread.Sleep(25);
            }
        }

        public void PrepFile(List<APBR_Patch_Mirror.read.FileDefinition> listDefinition)
        {
            int numGen = new Random().Next(0, Info.Webmirrors.Length);

            Info.CurrentMirror = Info.Webmirrors[numGen].ToString();

            var get = Info.ConvertPath(listDefinition[Info.integer].FileName);

            Directory.CreateDirectory(Path.GetDirectoryName(Info.filePath));

            if (Info.filePath.Contains("Launcher"))
            {
                if (Info.Webmirrors[numGen].Contains("gamersfirst.com"))
                {
                    Info.RemoteFile = Info.Webmirrors[numGen].ToString() + "Launcher" + Info.RemoteL_Ver + "/" + get + ".zip";
                    Fetch(Info.RemoteFile, get + ".zip");
                }
                else
                {
                    Info.RemoteFile = Info.Webmirrors[numGen].ToString() + get + ".zip";
                    Fetch(Info.RemoteFile, get + ".zip");
                }
            }
            else if (Info.Webmirrors[numGen].Contains("gamersfirst"))
            {
                Info.RemoteFile = Info.Webmirrors[numGen].ToString() + "Client" + Info.RemoteC_Ver + "/" + get + ".zip";
                Fetch(Info.RemoteFile, get + ".zip");
            }
            else
            {
                Info.RemoteFile = Info.Webmirrors[numGen].ToString() + get;
                Fetch(Info.RemoteFile, get + ".zip");
            }
        }

        private void SetProgressBarText(ProgressBar Target, ProgressBarTextLocation Location, Boolean IsDLBar = false, double kbs = 0)
        {
            string newText; var then = DateTime.Now; int kb = 0; double total;
            if (Target == null) { throw new ArgumentException("Null Target"); }
            int percent = (int)(((double)(Target.Value - Target.Minimum) / (double)(Target.Maximum - Target.Minimum)) * 100);

            if (IsDLBar)
            {
                DateTime now = new DateTime(); 
                total = (now - then).TotalSeconds + 3;
                if ((now - then).TotalSeconds > total)
                {
                    kb = (int)kbs;
                    newText = percent.ToString() + "% \n" + kb.ToString().Split('.')[0] + " KB/s";
                }
                else
                    newText = percent.ToString() + "% \n" + kb.ToString().Split('.')[0] + " KB/s";
            }
            else
                newText = percent.ToString() + "%";

            using (Graphics gr = Target.CreateGraphics())
            {
                gr.DrawString(newText, DefaultFont, new SolidBrush(Color.Black), new PointF(Location == ProgressBarTextLocation.Left ? 5 :
                    Target.Width / 2 - (gr.MeasureString(newText, DefaultFont).Width / 2.0F),
                    Target.Height / 2 - (gr.MeasureString(newText, DefaultFont).Height / 2.0F)));
            }
        }
        public enum ProgressBarTextLocation { Left, Centered }
    }
}